package com.example.aiagent.demo.invoke;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import cn.hutool.core.util.StrUtil;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversation;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationParam;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult;
import com.alibaba.dashscope.common.MultiModalMessage;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.exception.UploadFileException;
import com.esotericsoftware.kryo.Kryo;
import com.example.aiagent.app.KryoMultiWrite;
import com.example.aiagent.chatmemory.FileBasedChatMemory;
import com.example.aiagent.enums.ChatHistoryMessageTypeEnum;
import io.reactivex.Flowable;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.objenesis.strategy.StdInstantiatorStrategy;


public class VisionInvoke {

    private static final String SYSTEM_PROMPT =
            "**身份与核心使命：** 您是诺基亚企业专属的智能知识引擎与数字人助手。您的核心使命是深度处理企业知识资产，为全球员工提供即时、精准的知识服务，显著提升工作效率、优化决策流程、强化知识传承，最终赋能诺基亚在通信科技领域的持续创新与市场竞争力。\n" +

                    "**核心能力声明：**\n" +
                    "1.  **智能文档处理专家：** 精通解析多种办公文档格式（Word, PPT, Excel, PDF, TXT, Email），从中精准提取、结构化并索引关键信息（技术参数、项目报告、流程规范、会议纪要、市场分析等）。\n" +
                    "2.  **全面知识库构建者：** 基于处理的海量文档，动态构建、更新和维护覆盖诺基亚产品技术、研发流程、项目管理、市场策略、公司政策、历史案例等维度的统一企业知识图谱。\n" +
                    "3.  **精准智能问答引擎：** 基于构建的知识库，理解员工复杂、专业的自然语言查询（中英文），提供最相关、最准确、来源清晰的答案，支持多轮追问与上下文关联。\n" +
                    "4.  **高效信息导航员：** 不仅能回答问题，更能主动引导用户发现相关文档、知识点或负责人（如有权限信息），减少信息搜寻成本。\n" +

                    "**服务引导与用户交互：**\n" +
                    "1.  **开场引导：** 清晰表明身份（'诺基亚知识助手为您服务'），并简述核心能力（'我能帮您快速查找技术文档、解答产品疑问、理解流程规范、追溯项目历史。请告诉我您的需求'）。\n" +
                    "2.  **需求分类与深度挖掘：** 根据用户初始提问或场景，主动引导用户明确需求细节，以便提供最精准支持：\n" +
                    "    *   **技术/产品查询 (如：5G基站参数、路由器配置指南)：** 询问具体产品型号、软件版本、技术标准、遇到的具体问题或错误代码。\n" +
                    "    *   **流程/规范咨询 (如：项目立项流程、报销政策、安全守则)：** 询问涉及的具体流程环节、政策名称/编号、适用地域/部门、具体疑问点。\n" +
                    "    *   **历史信息/案例检索 (如：某项目经验总结、特定故障的解决方案)：** 询问项目名称/编号、时间范围、关键词、案例类型（成功/失败/风险）。\n" +
                    "    *   **文档定位 (如：寻找某次会议纪要、某份技术白皮书)：** 询问文档标题关键词、作者/部门、大致时间、文档类型。\n" +
                    "    *   **概念/术语解释 (如：解释'网络切片'、'SDN/NFV')：** 询问术语出现的具体上下文或需要了解的深度（基础概念/技术细节/应用场景）。\n" +
                    "3.  **引导详述：** 始终鼓励用户尽可能提供：\n" +
                    "    *   问题/需求的**具体背景**。\n" +
                    "    *   已查阅过哪些相关文档或信息（若有）。\n" +
                    "    *   需要答案达到的**详细程度**（概览/步骤/原理/示例）。\n" +
                    "    *   涉及的关键**名称/编号/代码**（产品、项目、文档、流程、错误码等）。\n" +

                    "**输出规范与价值强调：**\n" +
                    "1.  **答案精准与溯源：** 提供的答案必须严格基于诺基亚内部权威知识库，并清晰标注信息来源（文档标题、章节、链接等，如有）。无法确认时，明确告知并建议咨询渠道。\n" +
                    "2.  **结构化与清晰：** 答案组织逻辑清晰，重点突出。复杂问题分点阐述，必要时使用表格、代码块等增强可读性。技术术语提供简明解释。\n" +
                    "3.  **效率导向：** 回答力求简洁直接，直击要点，避免冗长无关信息。提供进一步探索的路径（如相关文档链接、知识点）。\n" +
                    "4.  **安全与合规：** 严格遵守诺基亚数据安全与保密政策。不处理、不透露任何敏感信息。提醒用户勿输入机密内容。\n" +
                    "5.  **价值重申：** 在交互中适时体现核心价值：'这能帮您节省查阅时间'、'此信息来自最新版规范'、'相关案例可参考XXX'、'这将有助于项目决策效率'。\n" +

                    "**终极目标：** 成为诺基亚员工不可或缺的智慧伙伴，通过无缝的知识获取与共享，加速问题解决，激发创新潜能，提升个人与组织效能，巩固诺基亚在激烈市场竞争中的技术领导力和运营卓越性。";

    public static final String fileDir = System.getProperty("user.dir") + "/chat-memory";
    public ChatMemory chatMemory = new FileBasedChatMemory(fileDir);
    public static final List<Message> messages = new ArrayList<>();

    private final static Kryo kryo = new Kryo();

    static {
        kryo.setRegistrationRequired(false);
        // 设置实例化策略
        kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
    }


    public Flowable<String> streamCall(String message, Long chatId, String url)
            throws ApiException, NoApiKeyException, UploadFileException, IOException {
        // 构造文件路径
        String filePath = fileDir + "/" + chatId + ".kryo";
        KryoMultiWrite kryoMultiWrite = new KryoMultiWrite();
        // 将用户消息写入会话记忆
        kryoMultiWrite.appendText(message, filePath);
        MultiModalConversation conv = new MultiModalConversation();
        // must create mutable map.
        MultiModalMessage systemMessage = MultiModalMessage.builder().role(Role.SYSTEM.getValue())
                .content(Arrays.asList(Collections.singletonMap("text", SYSTEM_PROMPT))).build();
        MultiModalMessage userMessage = MultiModalMessage.builder().role(Role.USER.getValue())
                .content(Arrays.asList(Collections.singletonMap("image", url),
                        Collections.singletonMap("text", message))).build();
        MultiModalConversationParam param = MultiModalConversationParam.builder()
                // 若没有配置环境变量，请用百炼API Key将下行替换为：.apiKey("sk-xxx")
                .apiKey(TestApiKey.API_KEY)
                .model("qwen-vl-max-latest")  // 此处以qwen-vl-max-latest为例，可按需更换模型名称。模型列表：https://help.aliyun.com/zh/model-studio/models
                .messages(Arrays.asList(systemMessage, userMessage))
                .incrementalOutput(true)
                .build();
        // 将AI传递的消息存入会话中
        StringBuilder aiResponseBuilder = new StringBuilder();
        Flowable<MultiModalConversationResult> result = conv.streamCall(param);
        return result.map(item->{
            String chunk = (String) item.getOutput().getChoices().get(0).getMessage().getContent().get(0).get("text");
            System.out.println(chunk);
            aiResponseBuilder.append(chunk);
            return chunk;
        })
                .doOnComplete(() -> {
                    // 当流结束时，完整响应已拼接完成
                    String fullResponse = aiResponseBuilder.toString();
                    // 可以在这里将完整响应写入会话记忆
                    kryoMultiWrite.appendText(fullResponse, filePath);
                });
    }

    public String simpleMultiModalConversationCall(String message, Long chatId, String url)
            throws ApiException, NoApiKeyException, UploadFileException, IOException {
        // 构造文件路径
        String filePath = fileDir + "/" + chatId + ".kryo";
        KryoMultiWrite kryoMultiWrite = new KryoMultiWrite();
        // 将用户消息写入会话记忆
        kryoMultiWrite.appendText(message, filePath);
        // 取出原先存储的会话记忆
        List<String> strings = kryoMultiWrite.readAllTexts(filePath);
        String formattedTexts = kryoMultiWrite.getFormattedTexts(strings);
        message = formattedTexts + "\n\n" + message;
        // 将用户消息写入会话记忆
        kryoMultiWrite.appendText(message, filePath);
        MultiModalConversation conv = new MultiModalConversation();
        MultiModalMessage systemMessage = MultiModalMessage.builder().role(Role.SYSTEM.getValue())
                .content(Arrays.asList(
                        Collections.singletonMap("text", SYSTEM_PROMPT))).build();
        MultiModalMessage userMessage = MultiModalMessage.builder().role(Role.USER.getValue())
                .content(Arrays.asList(
                        Collections.singletonMap("image", url),
                        Collections.singletonMap("text", message))).build();
        MultiModalConversationParam param = MultiModalConversationParam.builder()
                // 若没有配置环境变量，请用百炼API Key将下行替换为：.apiKey("sk-xxx")
                .apiKey(TestApiKey.API_KEY)
                .model("qwen-vl-max-latest")  // 此处以qwen-vl-max-latest为例，可按需更换模型名称。模型列表：https://help.aliyun.com/zh/model-studio/models
                .messages(Arrays.asList(systemMessage, userMessage))
                .build();
        MultiModalConversationResult result = conv.call(param);
        String content = (String) result.getOutput().getChoices().get(0).getMessage().getContent().get(0).get("text");
        kryoMultiWrite.appendText(content, filePath);
        return content;
    }

    public static void main(String[] args) {
//        try {
////            streamCall();
////            simpleMultiModalConversationCall();
//        } catch (ApiException | NoApiKeyException | UploadFileException e) {
//            System.out.println(e.getMessage());
//        }
//        System.exit(0);
    }
}